﻿using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace Notoric.View.Music.CWNotation.Controls
{
    /// <summary>
    /// Visualizes a rest.
    /// </summary>
    public partial class Rest : UserControl
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="Rest"/> instance.
        /// </summary>
        public Rest()
        {
            InitializeComponent();
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Occurs when the data context (visualized rest) has changed.
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// The handler unbinds from the old rest's <see cref="ViewModel.ViewModelElement.PropertyChanged"/>, 
        /// caches the new visualized rest, binds to its <see cref="ViewModel.ViewModelElement.PropertyChanged"/> event
        /// and calls <see cref="SetVisualization"/>.
        /// </remarks>
        /// <param name="sender">Object where the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void UserControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (rest != null)
                rest.PropertyChanged -= rest_PropertyChanged;

            rest = (ViewModel.Elements.CWNotation.Rest)DataContext;

            if (rest != null)
            {
                rest.PropertyChanged += new PropertyChangedEventHandler(rest_PropertyChanged);
                SetVisualization();
                SetRestPoint();
            }
        }

        /// <summary>
        /// Occurs when the value of some of the rest's properties has changed.
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// If the modified property is <see cref="Notoric.ViewModel.Elements.CWNotation.DurationalSymbol.BasicDuration"/>,
        /// the handler calls the <see cref="SetVisualization"/> method.
        /// </remarks>
        /// <param name="sender">Object that has sent the event</param>
        /// <param name="e">Event data</param>
        private void rest_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "BasicDuration")
            {
                SetVisualization();
                SetRestPoint();
            }
            else if (e.PropertyName == "VerticalPosition" || e.PropertyName == "Position")
                SetRestPoint();
        }

        /// <summary>
        /// Occurs when the contol has been loaded.
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// The handler caches the visualized rest, binds to its <see cref="ViewModel.ViewModelElement.PropertyChanged"/> event
        /// and calls <see cref="SetVisualization"/>.
        /// </remarks>
        /// <param name="sender">Object where the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            if (DataContext != null)
            {
                rest = (ViewModel.Elements.CWNotation.Rest)DataContext;
                rest.PropertyChanged += new PropertyChangedEventHandler(rest_PropertyChanged);

                SetVisualization();
                SetRestPoint();
            }
        }

        /// <summary>
        /// Handles the PreviewMouseLeftButtonDown event of the PART_RestSymbol control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        private void PART_RestSymbol_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (ApplicationBridge.Current.State.MouseTarget == typeof(MusicSectionInterior))
            {
                if (ApplicationBridge.Current.State.Command is ViewModel.AddDynamicMarkCommand)
                {
                    ApplicationBridge.Current.State.Command.SetParameterValue<ViewModel.Elements.CWNotation.DurationalSymbol>("Symbol", rest);
                    ApplicationBridge.Current.State.Command.Execute(null);
                    ApplicationBridge.Current.State.Command = null;
                    ApplicationBridge.Current.State.MouseTarget = null;
                    ApplicationBridge.Current.State.StatusBarMessage = "Ready";
                }
                else if (ApplicationBridge.Current.State.Command is ViewModel.AddDynamicsProgressMarkCommand)
                {
                    if (!ApplicationBridge.Current.State.Command.CanComputeParameterValue("Start", null))
                        ApplicationBridge.Current.State.Command.SetParameterValue<ViewModel.Elements.CWNotation.DurationalSymbol>("Start", rest);
                    else
                    {
                        ApplicationBridge.Current.State.Command.SetParameterValue<ViewModel.Elements.CWNotation.DurationalSymbol>("End", rest);
                        ApplicationBridge.Current.State.Command.Execute(null);
                        ApplicationBridge.Current.State.Command = null;
                        ApplicationBridge.Current.State.MouseTarget = null;
                        ApplicationBridge.Current.State.StatusBarMessage = "Ready";
                    }
                }
            }
            else
            {
                if (Keyboard.IsKeyUp(Key.LeftCtrl) && Keyboard.IsKeyUp(Key.RightCtrl))
                    rest.FindOwner<ViewModel.Elements.CWNotation.MusicSectionInterior>().ClearSelection();
                rest.FindOwner<ViewModel.Elements.CWNotation.MusicSectionInterior>().AddToSelection(rest);
            }
        }

        #endregion

        #region Visual Overrides

        /// <inheritdoc/>
        protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
        {
            var rect = new Rect(restPoint, restSize);

            if (rect.Contains(hitTestParameters.HitPoint))
                return new PointHitTestResult(PART_RestSymbol, hitTestParameters.HitPoint);

            return null;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Computes the top-left corner of the rest's bounding box.
        /// </summary>
        private void SetRestPoint()
        {
            if (rest == null)
                restPoint = new Point();

            switch (rest.BasicDuration)
            {
                case 128:
                    restPoint = new Point(rest.Position, rest.VerticalPosition - spaceSize / 2);
                    break;
                case 64:
                    restPoint = new Point(rest.Position, rest.VerticalPosition);
                    break;
                case 32:
                    restPoint = new Point(rest.Position, rest.VerticalPosition - spaceSize * 1.5);
                    break;
                case 16:
                    restPoint = new Point(rest.Position, rest.VerticalPosition - spaceSize);
                    break;
                case 8:
                    restPoint = new Point(rest.Position, rest.VerticalPosition - spaceSize);
                    break;
                case 4:
                    restPoint = new Point(rest.Position, rest.VerticalPosition - spaceSize * 2);
                    break;
                case 2:
                    restPoint = new Point(rest.Position, rest.VerticalPosition - spaceSize * 3);
                    break;
                case 1:
                    restPoint = new Point(rest.Position, rest.VerticalPosition - spaceSize * 4);
                    break;
            }
        }

        /// <summary>
        /// Selects the symbol and margin for the <see cref="PART_RestSymbol"/> control
        /// to visualize the rest's type.
        /// </summary>
        private void SetVisualization()
        {
            var staffSpaces = rest.FindOwner<ViewModel.Elements.CWNotation.Staff>().Lines.Count - 1;
            spaceSize = (staffSpaces > 0 ? rest.EffectiveTypeSize / staffSpaces : rest.FindOwner<ViewModel.Elements.CWNotation.Staff>().SpaceSize);

            switch (rest.BasicDuration)
            {
                case 128:
                    PART_RestSymbol.Text = "î";
                    PART_RestSymbol.Margin = new Thickness(0, -spaceSize * 5.5, 0, 0);
                    restSize = new Size(spaceSize, spaceSize / 2);
                    break;
                case 64:
                    PART_RestSymbol.Text = "î";
                    PART_RestSymbol.Margin = new Thickness(0, -spaceSize * 5.0, 0, 0);
                    restSize = new Size(spaceSize, spaceSize / 2);
                    break;
                case 32:
                    PART_RestSymbol.Text = "Î";
                    PART_RestSymbol.Margin = new Thickness(0, -spaceSize * 5.25, 0, 0);
                    restSize = new Size(spaceSize, spaceSize * 2.5);
                    break;
                case 16:
                    PART_RestSymbol.Text = "ä";
                    PART_RestSymbol.Margin = new Thickness(0, -spaceSize * 5.5, 0, 0);
                    restSize = new Size(spaceSize, spaceSize * 2);
                    break;
                case 8:
                    PART_RestSymbol.Text = "Å";
                    PART_RestSymbol.Margin = new Thickness(0, -spaceSize * 4.5, 0, 0);
                    restSize = new Size(spaceSize, spaceSize * 3);
                    break;
                case 4:
                    PART_RestSymbol.Text = "¨";
                    PART_RestSymbol.Margin = new Thickness(0, -spaceSize * 4.5, 0, 0);
                    restSize = new Size(spaceSize, spaceSize * 4);
                    break;
                case 2:
                    PART_RestSymbol.Text = "ô";
                    PART_RestSymbol.Margin = new Thickness(0, -spaceSize * 3.5, 0, 0);
                    restSize = new Size(spaceSize, spaceSize * 5);
                    break;
                case 1:
                    PART_RestSymbol.Text = "å";
                    PART_RestSymbol.Margin = new Thickness(0, -spaceSize * 3.5, 0, 0);
                    restSize = new Size(spaceSize, spaceSize * 6);
                    break;
            }

            PART_Dots.Margin = new Thickness(0, -5.5 * spaceSize, 0, 0);
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Caches the size of a staff space.
        /// </summary>
        private double spaceSize;
        /// <summary>
        /// Holds the visualized rest.
        /// </summary>
        private ViewModel.Elements.CWNotation.Rest rest;
        /// <summary>
        /// Caches the top-left corner of the rest's bounding rect.
        /// </summary>
        private Point restPoint;
        /// <summary>
        /// Caches the size of the rest's bounding rect.
        /// </summary>
        private Size restSize;

        #endregion
    }
}
